package com.suray.basic.wcs.utils.comm;

import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.basic.wcs.utils.WcsFuncUtil;
import com.suray.basic.wcs.utils.typedef.*;
import com.suray.basic.wcs.utils.typedef.Byte;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class BaseMessage implements BaseMessageIf {

    protected List<BaseDataType> baseDatas = new ArrayList<>();

    private String description;

    private long messgeTime;

    /**
     * 是否为大端，否则为小端
     * 0x1122
     * 大端：0x11 0x22
     * 小端：0x22 0x11
     */
    private boolean isBig;

    /**
     * @param description:报文的说明信息
     */
    public BaseMessage(String description, boolean isBig) {
        this.description = description;
        this.isBig = isBig;
    }

    /**
     * 获取该报文的说明信息
     *
     * @return
     */
    public String getDescription() {
        return description;
    }

    /**
     * 获取报文生成时间
     *
     * @return
     */
    public long getMessgeTime() {
        return messgeTime;
    }

    /**
     * 写入报文生成时间
     *
     * @param messgeTime
     */
    public void setMessgeTime(long messgeTime) {
        this.messgeTime = messgeTime;
    }

    /**
     * 解析报文
     *
     * @param message
     */
    public void readMessage(byte[] message) {
        boolean[] msg = new boolean[message.length * 8];

        int index = 0;
        for (byte data : message) {
            int offset = 1;
            for (int i = 0; i < 8; i++) {
                msg[index++] = ((data & offset) >> i) == 1;
                offset <<= 1;
            }
        }

        calcValues(msg);
    }

    /**
     * 把2进制数据解析成报文数据
     *
     * @param msg
     */
    private void calcValues(boolean[] msg) {
        int index = 0;

        for (BaseDataType baseData : baseDatas) {
            try {
                int data = 0;
                for (int i = 0; i < baseData.getSize(); i++) {
                    data += (msg[index++] ? 1 : 0) << i;
                }

                data = analyzeData(data, baseData);

                baseData.setParam(data);
            } catch (ArrayIndexOutOfBoundsException e) {
                LoggerUtil.error(e.getMessage(), this.getClass());
            }
        }
    }

    /**
     * 获取报文长度
     *
     * @return
     */
    public int getMessageSize() {
        int size = 0;

        for (BaseDataType baseData : baseDatas) {
            size += baseData.getSize();
        }

        return size / 8;
    }

    /**
     * 生成通讯报文
     *
     * @return
     */
    public byte[] makeMessage() {
        int length = baseDatas.stream().mapToInt(BaseDataType::getSize).sum();

        boolean[] message = new boolean[length];

        int index = 0;
        for (BaseDataType baseData : baseDatas) {
            int data = baseData.getParam();
            data = analyzeData(data, baseData);

            int offset = 1;
            for (int i = 0; i < baseData.getSize(); i++) {
                message[index++] = ((data & offset) >> i) != 0;
                offset <<= 1;
            }
        }

        return changeMessage(message);
    }

    private int analyzeData(int data, BaseDataType baseData) {
        if (isBig) {
            if (baseData instanceof Word) {
                data = WcsFuncUtil.changeWordFormat(data);
            } else if (baseData instanceof F_DWord) {
                //modbus dword解析
                data = WcsFuncUtil.changeDWordFormat(data);
                data = WcsFuncUtil.combineToDWord((data >> 16) & 0xFFFF, data & 0xFFFF);
            } else if (baseData instanceof DWord) {
                //正常dword解析
                data = WcsFuncUtil.changeDWordFormat(data);
            }
        }

        return data;
    }

    /**
     * 把2进制数据转换成通讯报文
     *
     * @param message
     * @return
     */
    private byte[] changeMessage(boolean[] message) {
        byte[] msg = new byte[((message.length) + 7) / 8];

        int index = 0;
        byte b = 0;
        for (int i = 0; i < message.length; i++) {
            b += (message[i] ? 1 : 0) << (i % 8);

            if ((i + 1) % 8 == 0) {
                msg[index++] = b;
                b = 0;
            }
        }

        return msg;
    }

    /**
     * 把值写入对应数据名称
     *
     * @param name：数据名称
     * @param value：值
     */
    public void setValue(String name, int value) {
        for (BaseDataType data : baseDatas) {
            if (data.getName().equals(name)) {
                data.setParam(value);
                return;
            }
        }
        LoggerUtil.warn(description + "-->找不到数据:" + name, this.getClass());
    }

    /**
     * 获取对应名称的值
     *
     * @param name：数据名称
     * @return
     */
    public Integer getValue(String name) {
        for (BaseDataType data : baseDatas) {
            if (data.getName().equals(name)) {
                return data.getParam();
            }
        }

        LoggerUtil.warn(description + "-->找不到数据:" + name, this.getClass());
        return null;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append(description);
        sb.append(", 数据{");
        for (BaseDataType data : baseDatas) {
            sb.append(data.getName() + ":");
            sb.append(data.getParam() + ",");
        }
        sb.append("}");
        return sb.toString();
    }
}
